package com.hc.java.hash;

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * Created by hyp on 2018/5/22.
 */
public final class Hash {

	/**
	 * 把message进行md5运算
	 *
	 * openssl验证:
	 *     命令: echo -n "message" | openssl dgst -md5
	 *     注意: echo如果不加-n选项, 会自动添加换行符, 导致结果不正确
	 *
	 * @param message 待hash的字符串
	 *
	 * @return 返回128位, 即数组长度为16
	 */
	public static byte[] md5(String message) {
		if (null == message || message.isEmpty()) {
			throw new IllegalArgumentException("message is empty!");
		}

		MessageDigest md5;
		try {
			md5 = MessageDigest.getInstance("MD5");
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException(e.getMessage());
		}

		md5.update(message.getBytes());
		byte[] hash = md5.digest();

		return hash;
	}

	/**
	 * 把message进行md5运算
	 *
	 * @param message 待hash的字符串
	 *
	 * @return 返回小写字母的md5字符串
	 */
	public static String md5LowerCase(String message) {
		byte[] hash = md5(message);

		return toHexLowerCase(hash);
	}

	/**
	 * 把message进行md5运算
	 *
	 * @param message 待hash的字符串
	 *
	 * @return 返回大写字母的md5字符串
	 */
	public static String md5UpperCase(String message) {
		byte[] hash = md5(message);

		return toHexUpperCase(hash);
	}

	/**
	 * 把message进行加salt的md5运算
	 *
	 * Hmac(Hash-based Message Authentication Code), 指的是基于哈希的消息认证码.
	 *
	 * HmacMD5 ≈ md5(salt + message)
	 *
	 * openssl验证:
	 *     命令:
	 *         1. echo -n "message" | openssl md5 -hex -mac HMAC -macopt 'key:password'
	 *         2. echo -n "message" | openssl mac -macopt 'key:password' -digest md5 HMAC
	 *     注意: echo如果不加-n选项, 会自动添加换行符, 导致结果不正确
	 *
	 * @param message 待hash的字符串
	 * @param salt 盐
	 *
	 * @return 返回128位, 即数组长度为16
	 */
	public static byte[] hmacMd5(String message, String salt) {
		// Message Authentication Code
		Mac mac;
		try {
			mac = Mac.getInstance("HmacMD5");
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException(e);
		}

		SecretKey key = new SecretKeySpec(salt.getBytes(StandardCharsets.UTF_8), "HmacMD5");
		try {
			mac.init(key);
		} catch (InvalidKeyException e) {
			throw new RuntimeException(e);
		}

		mac.update(message.getBytes(StandardCharsets.UTF_8));
		byte[] hash = mac.doFinal();

		return hash;
	}

	/**
	 * 把message进行加salt的md5运算
	 *
	 * @param message 待hash的字符串
	 * @param salt 盐
	 *
	 * @return 返回小写字母的md5字符串
	 */
	public static String hmacMd5LowerCase(String message, String salt) {
		byte[] hash = hmacMd5(message, salt);

		return toHexLowerCase(hash);
	}

	/**
	 * 把message进行加salt的md5运算
	 *
	 * @param message 待hash的字符串
	 * @param salt 盐
	 *
	 * @return 返回大写字母的md5字符串
	 */
	public static String hmacMd5UpperCase(String message, String salt) {
		byte[] hash = hmacMd5(message, salt);

		return toHexUpperCase(hash);
	}

	/**
	 * 把message进行sha1运算
	 * 属于SHA-1系列算法, SHA(Secure Hash Algorithm)
	 *
	 * @param message 待hash的字符串
	 *
	 * @return 返回160位, 即数组长度为20
	 */
	public static byte[] sha1(String message) {
		if (null == message || message.isEmpty()) {
			throw new IllegalArgumentException("message is empty!");
		}

		MessageDigest sha1;
		try {
			sha1 = MessageDigest.getInstance("SHA-1");
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException(e.getMessage());
		}

		sha1.update(message.getBytes());
		byte[] hash = sha1.digest();

		return hash;
	}

	/**
	 * 把message进行sha1运算
	 *
	 * @param message 待hash的字符串
	 *
	 * @return 返回小写字母的sha1字符串, 字符串长度为40
	 */
	public static String sha1LowerCase(String message) {
		byte[] hash = sha1(message);
		return toHexLowerCase(hash);
	}

	/**
	 * 把message进行sha1运算
	 *
	 * @param message 待hash的字符串
	 *
	 * @return 返回大写字母的sha1字符串, 字符串长度为40
	 */
	public static String sha1UpperCase(String message) {
		byte[] hash = sha1(message);
		return toHexUpperCase(hash);
	}

	/**
	 * 把message进行加salt的sha1运算
	 * 属于SHA-1系列算法
	 *
	 * Hmac(Hash-based Message Authentication Code), 指的是基于哈希的消息认证码.
	 *
	 * HmacSHA1 ≈ sha1(salt + message)
	 *
	 * openssl验证:
	 *     命令:
	 *         1. echo -n "message" | openssl sha1 -hex -mac HMAC -macopt 'key:password'
	 *         2. echo -n "message" | openssl mac -macopt 'key:password' -digest sha1 HMAC
	 *     注意: echo如果不加-n选项, 会自动添加换行符, 导致结果不正确
	 *
	 * @param message 待hash的字符串
	 * @param salt 盐
	 *
	 * @return 返回160位, 即数组长度为20
	 */
	public static byte[] hmacSha1(String message, String salt) {
		Mac mac;
		try {
			mac = Mac.getInstance("HmacSHA1");
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException(e);
		}

		SecretKey key = new SecretKeySpec(salt.getBytes(StandardCharsets.UTF_8), "HmacSHA1");
		try {
			mac.init(key);
		} catch (InvalidKeyException e) {
			throw new RuntimeException(e);
		}

		mac.update(message.getBytes(StandardCharsets.UTF_8));
		byte[] hash = mac.doFinal();

		return hash;
	}

	/**
	 * 把message进行加salt的sha1运算
	 *
	 * @param message 待hash的字符串
	 * @param salt 盐
	 *
	 * @return 返回小写字母的sha1字符串, 字符串长度为40
	 */
	public static String hmacSha1LowerCase(String message, String salt) {
		byte[] hash = hmacSha1(message, salt);
		return toHexLowerCase(hash);
	}

	/**
	 * 把message进行加salt的sha1运算
	 *
	 * @param message 待hash的字符串
	 * @param salt 盐
	 *
	 * @return 返回大写字母的sha1字符串, 字符串长度为40
	 */
	public static String hmacSha1UpperCase(String message, String salt) {
		byte[] hash = hmacSha1(message, salt);
		return toHexUpperCase(hash);
	}

	/**
	 * 把message进行sha224运算
	 * 属于SHA-2系列算法
	 *
	 * 该方法目前安全度已经很低, 国际上不推荐使用
	 *
	 * openssl验证:
	 *     命令: echo -n "message" | openssl dgst -sha224
	 *     注意: echo如果不加-n选项, 会自动添加换行符, 导致结果不正确
	 *
	 * @param message 待hash的字符串
	 *
	 * @return 返回224位, 即数组长度为28
	 */
	@Deprecated
	public static byte[] sha224(String message) {
		if (null == message || message.isEmpty()) {
			throw new IllegalArgumentException("message is empty!");
		}

		MessageDigest sha224;
		try {
			sha224 = MessageDigest.getInstance("SHA-224");
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException(e.getMessage());
		}

		sha224.update(message.getBytes());
		byte[] hash = sha224.digest();

		return hash;
	}

	/**
	 * 把message进行sha224运算
	 *
	 * 该方法目前安全度已经很低, 国际上不推荐使用
	 *
	 * @param message 待hash的字符串
	 *
	 * @return 返回小写字母的sha224字符串, 字符串长度为56
	 */
	@Deprecated
	public static String sha224LowerCase(String message) {
		byte[] hash = sha224(message);
		return toHexLowerCase(hash);
	}

	/**
	 * 把message进行sha224运算
	 *
	 * 该方法目前安全度已经很低, 国际上不推荐使用
	 *
	 * @param message 待hash的字符串
	 *
	 * @return 返回大写字母的sha224字符串, 字符串长度为56
	 */
	@Deprecated
	public static String sha224UpperCase(String message) {
		byte[] hash = sha224(message);
		return toHexUpperCase(hash);
	}

	/**
	 * 把message进行加salt的sha224运算
	 * 属于SHA-2系列算法
	 *
	 * Hmac(Hash-based Message Authentication Code), 指的是基于哈希的消息认证码.
	 *
	 * HmacSHA224 ≈ sha224(salt + message)
	 * 该方法目前安全度已经很低, 国际上不推荐使用
	 *
	 * openssl验证:
	 *     命令:
	 *         1. echo -n "message" | openssl sha224 -hex -mac HMAC -macopt 'key:password'
	 *         2. echo -n "message" | openssl mac -macopt 'key:password' -digest sha224 HMAC
	 *     注意: echo如果不加-n选项, 会自动添加换行符, 导致结果不正确
	 *
	 * @param message 待hash的字符串
	 * @param salt 盐
	 *
	 * @return 返回224位, 即数组长度为28
	 */
	public static byte[] hmacSha224(String message, String salt) {
		Mac mac;
		try {
			mac = Mac.getInstance("HmacSHA224");
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException(e);
		}

		SecretKey key = new SecretKeySpec(salt.getBytes(StandardCharsets.UTF_8), "HmacSHA224");
		try {
			mac.init(key);
		} catch (InvalidKeyException e) {
			throw new RuntimeException(e);
		}

		mac.update(message.getBytes(StandardCharsets.UTF_8));
		byte[] hash = mac.doFinal();

		return hash;
	}

	/**
	 * 把message进行加salt的sha224运算
	 *
	 * 该方法目前安全度已经很低, 国际上不推荐使用
	 *
	 * @param message 待hash的字符串
	 * @param salt 盐
	 *
	 * @return 返回小写字母的sha224字符串, 字符串长度为56
	 */
	public static String hmacSha224LowerCase(String message, String salt) {
		byte[] hash = hmacSha224(message, salt);
		return toHexLowerCase(hash);
	}

	/**
	 * 把message进行加salt的sha224运算
	 *
	 * 该方法目前安全度已经很低, 国际上不推荐使用
	 *
	 * @param message 待hash的字符串
	 * @param salt 盐
	 *
	 * @return 返回大写字母的sha224字符串, 字符串长度为56
	 */
	public static String hmacSha224UpperCase(String message, String salt) {
		byte[] hash = hmacSha224(message, salt);
		return toHexUpperCase(hash);
	}

	/**
	 * 把message进行sha256运算
	 * 属于SHA-2系列算法, SHA(Secure Hash Algorithm)
	 *
	 * openssl验证:
	 *     命令: echo -n "message" | openssl dgst -sha256
	 *     注意: echo如果不加-n选项, 会自动添加换行符, 导致结果不正确
	 *
	 * @param message 待hash的字符串
	 *
	 * @return 返回256位, 即数组长度为32
	 */
	public static byte[] sha256(String message) {
		if (null == message || message.isEmpty()) {
			throw new IllegalArgumentException("message is empty!");
		}

		MessageDigest sha256;
		try {
			sha256 = MessageDigest.getInstance("SHA-256");
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException(e.getMessage());
		}

		sha256.update(message.getBytes(StandardCharsets.UTF_8));
		byte[] hash = sha256.digest();

		return hash;
	}

	/**
	 * 把message进行sha256运算
	 *
	 * @param message 待hash的字符串
	 * @return 返回小写字母的sha256字符串, 字符串长度为64
	 */
	public static String sha256LowerCase(String message) {
		byte[] hash = sha256(message);
		return toHexLowerCase(hash);
	}

	/**
	 * 把message进行sha256运算
	 *
	 * @param message 待hash的字符串
	 *
	 * @return 返回大写字母的sha256字符串, 字符串长度为64
	 */
	public static String sha256UpperCase(String message) {
		byte[] hash = sha256(message);
		return toHexUpperCase(hash);
	}

	/**
	 * 把message进行加salt的sha256运算
	 * 属于SHA-2系列算法
	 *
	 * Hmac(Hash-based Message Authentication Code), 指的是基于哈希的消息认证码.
	 *
	 * HmacSHA256 ≈ sha256(salt + message)
	 *
	 * openssl验证:
	 *     命令:
	 *         1. echo -n "message" | openssl sha256 -hex -mac HMAC -macopt 'key:password'
	 *         2. echo -n "message" | openssl mac -macopt 'key:password' -digest sha256 HMAC
	 *     注意: echo如果不加-n选项, 会自动添加换行符, 导致结果不正确
	 *
	 * @param message 待hash的字符串
	 * @param salt 盐
	 *
	 * @return 返回256位, 即数组长度为32
	 */
	public static byte[] hmacSha256(String message, String salt) {
		Mac mac;
		try {
			mac = Mac.getInstance("HmacSHA256");
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException(e);
		}

		SecretKey key = new SecretKeySpec(salt.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
		try {
			mac.init(key);
		} catch (InvalidKeyException e) {
			throw new RuntimeException(e);
		}

		mac.update(message.getBytes(StandardCharsets.UTF_8));
		byte[] hash = mac.doFinal();

		return hash;
	}

	/**
	 * 把message进行加salt的sha256运算
	 *
	 * @param message 待hash的字符串
	 * @param salt 盐
	 *
	 * @return 返回小写字母的sha256字符串, 字符串长度为64
	 */
	public static String hmacSha256LowerCase(String message, String salt) {
		byte[] hash = hmacSha256(message, salt);
		return toHexLowerCase(hash);
	}

	/**
	 * 把message进行加salt的sha256运算
	 *
	 * @param message 待hash的字符串
	 * @param salt 盐
	 *
	 * @return 返回大写字母的sha256字符串, 字符串长度为64
	 */
	public static String hmacSha256UpperCase(String message, String salt) {
		byte[] hash = hmacSha256(message, salt);
		return toHexUpperCase(hash);
	}

	/**
	 * 把message进行sha384运算
	 * 属于SHA-2系列算法, SHA(Secure Hash Algorithm)
	 *
	 * openssl验证:
	 *     命令: echo -n "message" | openssl dgst -sha384
	 *     注意: echo如果不加-n选项, 会自动添加换行符, 导致结果不正确
	 *
	 * @param message 待hash的字符串
	 *
	 * @return 返回384位, 即数组长度为48
	 */
	public static byte[] sha384(String message) {
		if (null == message || message.isEmpty()) {
			throw new IllegalArgumentException("message is empty!");
		}

		MessageDigest sha384;
		try {
			sha384 = MessageDigest.getInstance("SHA-384");
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException(e.getMessage());
		}

		sha384.update(message.getBytes());
		byte[] hash = sha384.digest();

		return hash;
	}

	/**
	 * 把message进行sha384运算
	 *
	 * @param message 待hash的字符串
	 *
	 * @return 返回小写字母的sha384字符串, 字符串长度为96
	 */
	public static String sha384LowerCase(String message) {
		byte[] hash = sha384(message);
		return toHexLowerCase(hash);
	}

	/**
	 * 把message进行sha384运算
	 *
	 * @param message 待hash的字符串
	 *
	 * @return 返回大写字母的sha384字符串, 字符串长度为96
	 */
	public static String sha384UpperCase(String message) {
		byte[] hash = sha384(message);
		return toHexUpperCase(hash);
	}

	/**
	 * 把message进行加salt的sha384运算
	 * 属于SHA-2系列算法
	 *
	 * Hmac(Hash-based Message Authentication Code), 指的是基于哈希的消息认证码.
	 *
	 * HmacSHA384 ≈ sha384(salt + message)
	 *
	 * openssl验证:
	 *     命令:
	 *         1. echo -n "message" | openssl sha384 -hex -mac HMAC -macopt 'key:password'
	 *         2. echo -n "message" | openssl mac -macopt 'key:password' -digest sha384 HMAC
	 *     注意: echo如果不加-n选项, 会自动添加换行符, 导致结果不正确
	 *
	 * @param message 待hash的字符串
	 * @param salt 盐
	 *
	 * @return 返回384位, 即数组长度为48
	 */
	public static byte[] hmacSha384(String message, String salt) {
		Mac mac;
		try {
			mac = Mac.getInstance("HmacSHA384");
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException(e);
		}

		SecretKey key = new SecretKeySpec(salt.getBytes(StandardCharsets.UTF_8), "HmacSHA384");
		try {
			mac.init(key);
		} catch (InvalidKeyException e) {
			throw new RuntimeException(e);
		}

		mac.update(message.getBytes(StandardCharsets.UTF_8));
		byte[] hash = mac.doFinal();

		return hash;
	}

	/**
	 * 把message进行加salt的sha384运算
	 *
	 * @param message 待hash的字符串
	 * @param salt 盐
	 *
	 * @return 返回小写字母的sha384字符串, 字符串长度为96
	 */
	public static String hmacSha384LowerCase(String message, String salt) {
		byte[] hash = hmacSha384(message, salt);
		return toHexLowerCase(hash);
	}

	/**
	 * 把message进行加salt的sha384运算
	 *
	 * @param message 待hash的字符串
	 * @param salt 盐
	 *
	 * @return 返回大写字母的sha384字符串, 字符串长度为96
	 */
	public static String hmacSha384UpperCase(String message, String salt) {
		byte[] hash = hmacSha384(message, salt);
		return toHexUpperCase(hash);
	}

	/**
	 * 把message进行sha512运算
	 * 属于SHA-2系列算法, SHA(Secure Hash Algorithm)
	 *
	 * openssl验证:
	 *     命令: echo -n "message" | openssl dgst -sha512
	 *     注意: echo如果不加-n选项, 会自动添加换行符, 导致结果不正确
	 *
	 * @param message 待hash的字符串
	 *
	 * @return 返回512位, 即数组长度为64
	 */
	public static byte[] sha512(String message) {
		if (null == message || message.isEmpty()) {
			throw new IllegalArgumentException("message is empty!");
		}

		MessageDigest sha512;
		try {
			sha512 = MessageDigest.getInstance("SHA-512");
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException(e.getMessage());
		}

		sha512.update(message.getBytes());
		byte[] hash = sha512.digest();

		return hash;
	}

	/**
	 * 把message进行sha512运算
	 *
	 * @param message 待hash的字符串
	 *
	 * @return 返回小写字母的sha512字符串, 字符串长度为128
	 */
	public static String sha512LowerCase(String message) {
		byte[] hash = sha512(message);
		return toHexLowerCase(hash);
	}

	/**
	 * 把message进行sha512运算
	 *
	 * @param message 待hash的字符串
	 *
	 * @return 返回大写字母的sha512字符串, 字符串长度为128
	 */
	public static String sha512UpperCase(String message) {
		byte[] hash = sha512(message);
		return toHexUpperCase(hash);
	}

	/**
	 * 把message进行加salt的sha512运算
	 * 属于SHA-2系列算法
	 *
	 * Hmac(Hash-based Message Authentication Code), 指的是基于哈希的消息认证码.
	 *
	 * HmacSHA512 ≈ sha512(salt + message)
	 *
	 * openssl验证:
	 *     命令:
	 *         1. echo -n "message" | openssl sha512 -hex -mac HMAC -macopt 'key:password'
	 *         2. echo -n "message" | openssl mac -macopt 'key:password' -digest sha512 HMAC
	 *     注意: echo如果不加-n选项, 会自动添加换行符, 导致结果不正确
	 *
	 * @param message 待hash的字符串
	 * @param salt 盐
	 *
	 * @return 返回512位, 即数组长度为64
	 */
	public static byte[] hmacSha512(String message, String salt) {
		Mac mac;
		try {
			mac = Mac.getInstance("HmacSHA512");
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException(e);
		}

		SecretKey key = new SecretKeySpec(salt.getBytes(StandardCharsets.UTF_8), "HmacSHA512");
		try {
			mac.init(key);
		} catch (InvalidKeyException e) {
			throw new RuntimeException(e);
		}

		mac.update(message.getBytes(StandardCharsets.UTF_8));
		byte[] hash = mac.doFinal();

		return hash;
	}

	/**
	 * 把message进行加salt的sha512运算
	 *
	 * @param message 待hash的字符串
	 * @param salt 盐
	 *
	 * @return 返回小写字母的sha512字符串, 字符串长度为128
	 */
	public static String hmacSha512LowerCase(String message, String salt) {
		byte[] hash = hmacSha512(message, salt);
		return toHexLowerCase(hash);
	}

	/**
	 * 把message进行加salt的sha512运算
	 *
	 * @param message 待hash的字符串
	 * @param salt 盐
	 *
	 * @return 返回大写字母的sha512字符串, 字符串长度为128
	 */
	public static String hmacSha512UpperCase(String message, String salt) {
		byte[] hash = hmacSha512(message, salt);
		return toHexUpperCase(hash);
	}

	/**
	 * 把message进行sha3-224运算
	 * 属于SHA-3系列算法, SHA(Secure Hash Algorithm)
	 *
	 * openssl验证:
	 *     命令: echo -n "message" | openssl dgst -sha3-224
	 *     注意: echo如果不加-n选项, 会自动添加换行符, 导致结果不正确
	 *
	 * @param message 待hash的字符串
	 *
	 * @return 返回224位, 即数组长度为28
	 */
	public static byte[] sha3224(String message) {
		if (null == message || message.isEmpty()) {
			throw new IllegalArgumentException("message is empty!");
		}

		MessageDigest sha3224;
		try {
			sha3224 = MessageDigest.getInstance("SHA3-224");
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException(e.getMessage());
		}

		sha3224.update(message.getBytes());
		byte[] hash = sha3224.digest();

		return hash;
	}

	/**
	 * 把message进行sha3-224运算
	 *
	 * 该方法目前安全度已经很低, 国际上不推荐使用
	 *
	 * @param message 待hash的字符串
	 *
	 * @return 返回小写字母的sha3-224字符串, 字符串长度为56
	 */
	public static String sha3224LowerCase(String message) {
		byte[] hash = sha3224(message);
		return toHexLowerCase(hash);
	}

	/**
	 * 把message进行sha3-224运算
	 *
	 * 该方法目前安全度已经很低, 国际上不推荐使用
	 *
	 * @param message 待hash的字符串
	 *
	 * @return 返回大写字母的sha3-224字符串, 字符串长度为56
	 */
	public static String sha3224UpperCase(String message) {
		byte[] hash = sha3224(message);
		return toHexUpperCase(hash);
	}

	/**
	 * 把message进行加salt的sha3-224运算
	 * 属于SHA-3系列算法, 采用Keccak算法
	 *
	 * Hmac(Hash-based Message Authentication Code), 指的是基于哈希的消息认证码.
	 *
	 * HmacSHA3-224 ≈ sha3-224(salt + message)
	 *
	 * openssl验证:
	 *     命令:
	 *         1. echo -n "message" | openssl sha3-224 -hex -mac HMAC -macopt 'key:password'
	 *         2. echo -n "message" | openssl mac -macopt 'key:password' -digest sha3-224 HMAC
	 *     注意: echo如果不加-n选项, 会自动添加换行符, 导致结果不正确
	 *
	 * @param message 待hash的字符串
	 * @param salt 盐
	 *
	 * @return 返回224位, 即数组长度为28
	 */
	public static byte[] hmacSha3224(String message, String salt) {
		Mac mac;
		try {
			mac = Mac.getInstance("HmacSHA3-224");
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException(e);
		}

		SecretKey key = new SecretKeySpec(salt.getBytes(StandardCharsets.UTF_8), "HmacSHA3-224");
		try {
			mac.init(key);
		} catch (InvalidKeyException e) {
			throw new RuntimeException(e);
		}

		mac.update(message.getBytes(StandardCharsets.UTF_8));
		byte[] hash = mac.doFinal();

		return hash;
	}

	/**
	 * 把message进行加salt的sha224运算
	 *
	 * 该方法目前安全度已经很低, 国际上不推荐使用
	 *
	 * @param message 待hash的字符串
	 * @param salt 盐
	 *
	 * @return 返回小写字母的sha3-224字符串, 字符串长度为56
	 */
	public static String hmacSha3224LowerCase(String message, String salt) {
		byte[] hash = hmacSha3224(message, salt);
		return toHexLowerCase(hash);
	}

	/**
	 * 把message进行加salt的sha3-224运算
	 *
	 * 该方法目前安全度已经很低, 国际上不推荐使用
	 *
	 * @param message 待hash的字符串
	 * @param salt 盐
	 *
	 * @return 返回大写字母的sha3-224字符串, 字符串长度为56
	 */
	public static String hmacSha3224UpperCase(String message, String salt) {
		byte[] hash = hmacSha3224(message, salt);
		return toHexUpperCase(hash);
	}

	/**
	 * 把message进行sha3-256运算
	 * 属于SHA-3系列算法, SHA(Secure Hash Algorithm)
	 *
	 * openssl验证:
	 *     命令: echo -n "message" | openssl dgst -sha3-256
	 *     注意: echo如果不加-n选项, 会自动添加换行符, 导致结果不正确
	 *
	 * @param message 待hash的字符串
	 *
	 * @return 返回256位, 即数组长度为32
	 */
	public static byte[] sha3256(String message) {
		if (null == message || message.isEmpty()) {
			throw new IllegalArgumentException("message is empty!");
		}

		MessageDigest sha3256;
		try {
			sha3256 = MessageDigest.getInstance("SHA3-256");
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException(e.getMessage());
		}

		sha3256.update(message.getBytes());
		byte[] hash = sha3256.digest();

		return hash;
	}

	/**
	 * 把message进行sha3-256运算
	 *
	 * @param message 待hash的字符串
	 * @return 返回小写字母的sha3-256字符串, 字符串长度为64
	 */
	public static String sha3256LowerCase(String message) {
		byte[] hash = sha3256(message);
		return toHexLowerCase(hash);
	}

	/**
	 * 把message进行sha3-256运算
	 *
	 * @param message 待hash的字符串
	 *
	 * @return 返回大写字母的sha3-256字符串, 字符串长度为64
	 */
	public static String sha3256UpperCase(String message) {
		byte[] hash = sha3256(message);
		return toHexUpperCase(hash);
	}

	/**
	 * 把message进行加salt的sha3-256运算
	 * 属于SHA-2系列算法, 采用Keccak算法
	 *
	 * Hmac(Hash-based Message Authentication Code), 指的是基于哈希的消息认证码.
	 *
	 * HmacSHA3-256 ≈ sha3-256(salt + message)
	 *
	 * openssl验证:
	 *     命令:
	 *         1. echo -n "message" | openssl sha3-256 -hex -mac HMAC -macopt 'key:password'
	 *         2. echo -n "message" | openssl mac -macopt 'key:password' -digest sha3-256 HMAC
	 *     注意: echo如果不加-n选项, 会自动添加换行符, 导致结果不正确
	 *
	 * @param message 待hash的字符串
	 * @param salt 盐
	 *
	 * @return 返回256位, 即数组长度为32
	 */
	public static byte[] hmacSha3256(String message, String salt) {
		Mac mac;
		try {
			mac = Mac.getInstance("HmacSHA3-256");
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException(e);
		}

		SecretKey key = new SecretKeySpec(salt.getBytes(StandardCharsets.UTF_8), "HmacSHA3-256");
		try {
			mac.init(key);
		} catch (InvalidKeyException e) {
			throw new RuntimeException(e);
		}

		mac.update(message.getBytes(StandardCharsets.UTF_8));
		byte[] hash = mac.doFinal();

		return hash;
	}

	/**
	 * 把message进行加salt的sha3-256运算
	 *
	 * @param message 待hash的字符串
	 * @param salt 盐
	 *
	 * @return 返回小写字母的sha3-256字符串, 字符串长度为64
	 */
	public static String hmacSha3256LowerCase(String message, String salt) {
		byte[] hash = hmacSha3256(message, salt);
		return toHexLowerCase(hash);
	}

	/**
	 * 把message进行加salt的sha3-256运算
	 *
	 * @param message 待hash的字符串
	 * @param salt 盐
	 *
	 * @return 返回大写字母的sha3-256字符串, 字符串长度为64
	 */
	public static String hmacSha3256UpperCase(String message, String salt) {
		byte[] hash = hmacSha3256(message, salt);
		return toHexUpperCase(hash);
	}

	/**
	 * 把message进行sha3-384运算
	 * 属于SHA-3系列算法, SHA(Secure Hash Algorithm)
	 *
	 * openssl验证:
	 *     命令: echo -n "message" | openssl dgst -sha3-384
	 *     注意: echo如果不加-n选项, 会自动添加换行符, 导致结果不正确
	 *
	 * @param message 待hash的字符串
	 *
	 * @return 返回384位, 即数组长度为48
	 */
	public static byte[] sha3384(String message) {
		if (null == message || message.isEmpty()) {
			throw new IllegalArgumentException("message is empty!");
		}

		MessageDigest sha3384;
		try {
			sha3384 = MessageDigest.getInstance("SHA3-384");
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException(e.getMessage());
		}

		sha3384.update(message.getBytes());
		byte[] hash = sha3384.digest();

		return hash;
	}

	/**
	 * 把message进行sha3-384运算
	 *
	 * @param message 待hash的字符串
	 *
	 * @return 返回小写字母的sha3-384字符串, 字符串长度为96
	 */
	public static String sha3384LowerCase(String message) {
		byte[] hash = sha3384(message);
		return toHexLowerCase(hash);
	}

	/**
	 * 把message进行sha3-384运算
	 *
	 * @param message 待hash的字符串
	 *
	 * @return 返回大写字母的sha3-384字符串, 字符串长度为96
	 */
	public static String sha3384UpperCase(String message) {
		byte[] hash = sha3384(message);
		return toHexUpperCase(hash);
	}

	/**
	 * 把message进行加salt的sha3-384运算
	 * 属于SHA-3系列算法, 采用Keccak算法
	 *
	 * Hmac(Hash-based Message Authentication Code), 指的是基于哈希的消息认证码.
	 *
	 * HmacSHA3-384 ≈ sha3-384(salt + message)
	 *
	 * openssl验证:
	 *     命令:
	 *         1. echo -n "message" | openssl sha3-384 -hex -mac HMAC -macopt 'key:password'
	 *         2. echo -n "message" | openssl mac -macopt 'key:password' -digest sha3-384 HMAC
	 *     注意: echo如果不加-n选项, 会自动添加换行符, 导致结果不正确
	 *
	 * @param message 待hash的字符串
	 * @param salt 盐
	 *
	 * @return 返回384位, 即数组长度为48
	 */
	public static byte[] hmacSha3384(String message, String salt) {
		Mac mac;
		try {
			mac = Mac.getInstance("HmacSHA3-384");
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException(e);
		}

		SecretKey key = new SecretKeySpec(salt.getBytes(StandardCharsets.UTF_8), "HmacSHA3-384");
		try {
			mac.init(key);
		} catch (InvalidKeyException e) {
			throw new RuntimeException(e);
		}

		mac.update(message.getBytes(StandardCharsets.UTF_8));
		byte[] hash = mac.doFinal();

		return hash;
	}

	/**
	 * 把message进行加salt的sha3-384运算
	 *
	 * @param message 待hash的字符串
	 * @param salt 盐
	 *
	 * @return 返回小写字母的sha3-384字符串, 字符串长度为96
	 */
	public static String hmacSha3384LowerCase(String message, String salt) {
		byte[] hash = hmacSha3384(message, salt);
		return toHexLowerCase(hash);
	}

	/**
	 * 把message进行加salt的sha3-384运算
	 *
	 * @param message 待hash的字符串
	 * @param salt 盐
	 *
	 * @return 返回大写字母的sha3-384字符串, 字符串长度为96
	 */
	public static String hmacSha3384UpperCase(String message, String salt) {
		byte[] hash = hmacSha3384(message, salt);
		return toHexUpperCase(hash);
	}

	/**
	 * 把message进行sha3-512运算
	 * 属于SHA-3系列算法, SHA(Secure Hash Algorithm)
	 *
	 * openssl验证:
	 *     命令: echo -n "message" | openssl dgst -sha3-512
	 *     注意: echo如果不加-n选项, 会自动添加换行符, 导致结果不正确
	 *
	 * @param message 待hash的字符串
	 *
	 * @return 返回512位, 即数组长度为64
	 */
	public static byte[] sha3512(String message) {
		if (null == message || message.isEmpty()) {
			throw new IllegalArgumentException("message is empty!");
		}

		MessageDigest sha3512;
		try {
			sha3512 = MessageDigest.getInstance("SHA3-512");
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException(e.getMessage());
		}

		sha3512.update(message.getBytes());
		byte[] hash = sha3512.digest();

		return hash;
	}

	/**
	 * 把message进行sha3-512运算
	 *
	 * @param message 待hash的字符串
	 *
	 * @return 返回小写字母的sha3-512字符串, 字符串长度为128
	 */
	public static String sha3512LowerCase(String message) {
		byte[] hash = sha3512(message);
		return toHexLowerCase(hash);
	}

	/**
	 * 把message进行sha3-512运算
	 *
	 * @param message 待hash的字符串
	 *
	 * @return 返回大写字母的sha3-512字符串, 字符串长度为128
	 */
	public static String sha3512UpperCase(String message) {
		byte[] hash = sha3512(message);
		return toHexUpperCase(hash);
	}

	/**
	 * 把message进行加salt的sha3-512运算
	 * 属于SHA-3系列算法, 采用Keccak算法
	 *
	 * Hmac(Hash-based Message Authentication Code), 指的是基于哈希的消息认证码.
	 *
	 * HmacSHA3-512 ≈ sha3-512(salt + message)
	 *
	 * openssl验证:
	 *     命令:
	 *         1. echo -n "message" | openssl sha3-512 -hex -mac HMAC -macopt 'key:password'
	 *         2. echo -n "message" | openssl mac -macopt 'key:password' -digest sha3-512 HMAC
	 *     注意: echo如果不加-n选项, 会自动添加换行符, 导致结果不正确
	 *
	 * @param message 待hash的字符串
	 * @param salt 盐
	 *
	 * @return 返回512位, 即数组长度为64
	 */
	public static byte[] hmacSha3512(String message, String salt) {
		Mac mac;
		try {
			mac = Mac.getInstance("HmacSHA3-512");
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException(e);
		}

		SecretKey key = new SecretKeySpec(salt.getBytes(StandardCharsets.UTF_8), "HmacSHA3-512");
		try {
			mac.init(key);
		} catch (InvalidKeyException e) {
			throw new RuntimeException(e);
		}

		mac.update(message.getBytes(StandardCharsets.UTF_8));
		byte[] hash = mac.doFinal();

		return hash;
	}

	/**
	 * 把message进行加salt的sha3-512运算
	 *
	 * @param message 待hash的字符串
	 * @param salt 盐
	 *
	 * @return 返回小写字母的sha3-512字符串, 字符串长度为128
	 */
	public static String hmacSha3512LowerCase(String message, String salt) {
		byte[] hash = hmacSha3512(message, salt);
		return toHexLowerCase(hash);
	}

	/**
	 * 把message进行加salt的sha3-512运算
	 *
	 * @param message 待hash的字符串
	 * @param salt 盐
	 *
	 * @return 返回大写字母的sha3-512字符串, 字符串长度为128
	 */
	public static String hmacSha3512UpperCase(String message, String salt) {
		byte[] hash = hmacSha3512(message, salt);
		return toHexUpperCase(hash);
	}

	/**
	 * 把byte数字转换为16进制小写字符串
	 *
	 * @param hash 待hash的字符串
	 * @return hash后小写的字符串
	 */
	public static String toHexLowerCase(byte[] hash) {
		if (null == hash) {
			return null;
		}

		if (0 == hash.length) {
			return "";
		}

		//[0-9]       == [0-9]              == [0b0000_0000-0b0000_1001]
		//['0'-'9']   == [48-57]            == [0b0011_0000-0b0011_1001]
		//[0-16]      == [0-16]             == [0b0000_0000-0b0000_1111]
		//['0'-'F']   == [48-57] + [65-79]  == [0b0011_0000-0b0011_1001] + [0b0100_0001-0b0100_1111]
		//['0'-'f']   == [48-57] + [97-102] == [0b0011_0000-0b0011_1001] + [0b0110_0001-0b0110_1111]
		//['A'-'Z']   == [65-90]            == [0b0100_0001-0b0101_1010]
		//['a'-'z']   == [97-122]           == [0b0110_0001-0b0111_1010]
		char[] hex = new char[hash.length << 1];
		for (int i = 0; i < hash.length; i++) {
			int idx = i << 1;

			int high = (hash[i] & 0b11110000) >> 4;
			if (high < 10) {
				hex[idx] = (char) (high | 48); //变成字符0-9
			} else {
				hex[idx] = (char) (high + 87); //变成字符a-f
			}

			int low = hash[i] & 0b00001111;
			if (low < 10) {
				hex[idx + 1] = (char) (low | 48); //变成字符0-9
			} else {
				hex[idx + 1] = (char) (low + 87); //变成字符a-f
			}
		}

		return new String(hex);
	}

	/**
	 * 把byte数字转换为16进制大写字符串
	 *
	 * @param hash 待hash的字符串
	 * @return hash后大写的字符串
	 */
	public static String toHexUpperCase(byte[] hash) {
		if (null == hash) {
			return null;
		}

		if (0 == hash.length) {
			return "";
		}

		//[0-9]       == [0-9]              == [0b0000_0000-0b0000_1001]
		//['0'-'9']   == [48-57]            == [0b0011_0000-0b0011_1001]
		//[0-16]      == [0-16]             == [0b0000_0000-0b0000_1111]
		//['0'-'F']   == [48-57] + [65-79]  == [0b0011_0000-0b0011_1001] + [0b0100_0001-0b0100_1111]
		//['0'-'f']   == [48-57] + [97-102] == [0b0011_0000-0b0011_1001] + [0b0110_0001-0b0110_1111]
		//['A'-'Z']   == [65-90]            == [0b0100_0001-0b0101_1010]
		//['a'-'z']   == [97-122]           == [0b0110_0001-0b0111_1010]
		char[] hex = new char[hash.length << 1];
		for (int i = 0; i < hash.length; i++) {
			int idx = i << 1;

			int high = (hash[i] & 0b11110000) >> 4;
			if (high < 10) {
				hex[idx] = (char) (high | 48); //变成字符0-9
			} else {
				hex[idx] = (char) (high + 55); //变成字符A-F
			}

			int low = hash[i] & 0b00001111;
			if (low < 10) {
				hex[idx + 1] = (char) (low | 48); //变成字符0-9
			} else {
				hex[idx + 1] = (char) (low + 55); //变成字符A-F
			}
		}

		return new String(hex);
	}

	/**
	 * 把hex字符串转换为byte数组
	 *
	 * @param hex 待转换的hex字符串
	 * @return 转换后的byte数组
	 */
	public static byte[] toHexBytes(String hex) {
		if (null == hex || hex.isEmpty()) {
			return new byte[0];
		}

		int length = hex.length();
		char[] data = hex.toCharArray();

		//[0-9]       == [0-9]              == [0b0000_0000-0b0000_1001]
		//['0'-'9']   == [48-57]            == [0b0011_0000-0b0011_1001]
		//[0-16]      == [0-16]             == [0b0000_0000-0b0000_1111]
		//['0'-'F']   == [48-57] + [65-79]  == [0b0011_0000-0b0011_1001] + [0b0100_0001-0b0100_1111]
		//['0'-'f']   == [48-57] + [97-102] == [0b0011_0000-0b0011_1001] + [0b0110_0001-0b0110_1111]
		//['A'-'Z']   == [65-90]            == [0b0100_0001-0b0101_1010]
		//['a'-'z']   == [97-122]           == [0b0110_0001-0b0111_1010]
		byte[] bytes = new byte[length >> 1];
		for (int i = 0; i < length; i += 2) {
			int idx = i >> 1;

			char hChar = data[i];
			int high;
			if (hChar >= '0' && hChar <= '9') {
				high = hChar & 0b00001111;
			} else if (hChar >= 'A' && hChar <= 'F') {
				high = hChar - 55;
			} else if (hChar >= 'a' && hChar <= 'f') {
				high = hChar - 87;
			} else {
				throw new IllegalArgumentException("invalid hex char " + hChar);
			}

			char lChar = data[i + 1];
			int low;
			if (lChar >= '0' && lChar <= '9') {
				low = lChar & 0b00001111;
			} else if (lChar >= 'A' && lChar <= 'F') {
				low = lChar - 55;
			} else if (lChar >= 'a' && lChar <= 'f') {
				low = lChar - 87;
			} else {
				throw new IllegalArgumentException("invalid hex char " + lChar);
			}

			bytes[idx] = (byte) ((high << 4) | low);
		}

		return bytes;
	}

	public static void main(String[] args) {
		String message = "hello world";
		String salt = "1234567";
		String bytes = Hash.hmacSha256UpperCase(message, salt);
		System.out.println(bytes); // 83B3EB2788457B46A2F17AAA048F795AF0D9DABB8E5924DD2FC0EA682D929FE5
	}// 06927A1EE9440C22DEE90F2DBABAA7DD8CFF23852125FC36EE4D5C4F8EE35B2E
}
